#!/bin/sh
# shellcheck disable=SC2039
# shellcheck disable=SC2120
# shellcheck disable=SC2164

export IMAGE_TAG="${IMAGE_TAG:-main-jvm}"
export NATIVE_IMAGE_TAG="${IMAGE_TAG%-jvm}"
export ADMINUI_IMAGE_TAG="$NATIVE_IMAGE_TAG"
export OPERATOR_PUBLIC_REGISTRY="${OPERATOR_PUBLIC_REGISTRY:-quay.io}"
export OPERATOR_PUBLIC_REGISTRY_PATH="${OPERATOR_PUBLIC_REGISTRY_PATH:-/stackgres}"
export COMPONENT_PUBLIC_REGISTRY="${COMPONENT_PUBLIC_REGISTRY:-quay.io}"
export COMPONENT_PUBLIC_REGISTRY_PATH="${COMPONENT_PUBLIC_REGISTRY_PATH:-/ongres}"
export OPERATOR_IMAGE_NAME="${OPERATOR_PUBLIC_REGISTRY}${OPERATOR_PUBLIC_REGISTRY_PATH}/operator:$IMAGE_TAG"
export RESTAPI_IMAGE_NAME="${OPERATOR_PUBLIC_REGISTRY}${OPERATOR_PUBLIC_REGISTRY_PATH}/restapi:$IMAGE_TAG"
export JOBS_IMAGE_NAME="${OPERATOR_PUBLIC_REGISTRY}${OPERATOR_PUBLIC_REGISTRY_PATH}/jobs:$IMAGE_TAG"
export CLUSTER_CONTROLLER_IMAGE_NAME="${OPERATOR_PUBLIC_REGISTRY}${OPERATOR_PUBLIC_REGISTRY_PATH}/cluster-controller:$IMAGE_TAG"
export DISTRIBUTEDLOGS_CONTROLLER_IMAGE_NAME="${OPERATOR_PUBLIC_REGISTRY}${OPERATOR_PUBLIC_REGISTRY_PATH}/distributedlogs-controller:$IMAGE_TAG"
export STREAM_IMAGE_NAME="${OPERATOR_PUBLIC_REGISTRY}${OPERATOR_PUBLIC_REGISTRY_PATH}/stream:${NATIVE_IMAGE_TAG}-jvm"
export ADMINUI_IMAGE_NAME="${OPERATOR_PUBLIC_REGISTRY}${OPERATOR_PUBLIC_REGISTRY_PATH}/admin-ui:$ADMINUI_IMAGE_TAG"
export PGLAMBDA_JAVASCRIPT_IMAGE_NAME="${OPERATOR_PUBLIC_REGISTRY}${OPERATOR_PUBLIC_REGISTRY_PATH}/pglambda:${NATIVE_IMAGE_TAG}-javascript"

export STACKGRES_PATH="${STACKGRES_PATH:-$E2E_PATH/..}"
export STACKGRES_IMAGE_BUILDER="${STACKGRES_IMAGE_BUILDER:-docker}"
export OPERATOR_CHART_PATH="${OPERATOR_CHART_PATH:-"$STACKGRES_PATH/install/helm/stackgres-operator/"}"
export CLUSTER_CHART_PATH="${CLUSTER_CHART_PATH:-"$STACKGRES_PATH/install/helm/stackgres-cluster/"}"
export UI_TESTS_RESOURCES_PATH="${UI_TESTS_RESOURCES_PATH:-"$STACKGRES_PATH/src/admin-ui"}"
export STACKGRES_HELM_PATH="${STACKGRES_PATH}/install/helm"
export STACKGRES_PACKAGES="${STACKGRES_HELM_PATH}/target/packages"
export STACKGRES_YAMLS="${STACKGRES_HELM_PATH}/target/templates"

export E2E_SKIP_SETUP="${E2E_SKIP_SETUP:-false}"
export E2E_OPERATOR_PULL_POLICY="${E2E_OPERATOR_PULL_POLICY:-Never}"
export E2E_BUILD_IMAGES="${E2E_BUILD_IMAGES:-true}"
export E2E_SKIP_OPERATOR_LOAD="${E2E_SKIP_OPERATOR_LOAD:-false}"
export E2E_WAIT_OPERATOR="${E2E_WAIT_OPERATOR:-true}"

export E2E_INCLUDE_ONLY_POSTGRES_VERSIONS="12.16 12.18 13.9 13.12 14.9 14.10 15.3 15.5 16.1 16.2 ${E2E_POSTGRES_VERSION:-15.3} $E2E_UI_POSTGRES_VERSOIN"

export STACKGRES_PREVIOUS_VERSION="${STACKGRES_PREVIOUS_VERSION:-1.17.4}"

export E2E_LOCAL_OPERATOR_IP="${E2E_LOCAL_OPERATOR_IP:-172.20.0.1}"
export E2E_LOCAL_OPERATOR_PORT="${E2E_LOCAL_OPERATOR_PORT:-9080}"
export E2E_LOCAL_OPERATOR_SSL_PORT="${E2E_LOCAL_OPERATOR_SSL_PORT:-9443}"

generate_operator_namespace() {
  echo "stackgres-$(printf '%x' "$(date +%s)")"
}

export OPERATOR_NAMESPACE="${OPERATOR_NAMESPACE:-$(generate_operator_namespace)}"

setup_images() {
  if {
      [ "$E2E_BUILD_IMAGES" = true ] \
        || [ "$E2E_BUILD_ONLY_MODULES" = true ] 
    } \
    && {
      [ "$E2E_REUSE_OPERATOR_PODS" != true ] \
        || ! is_operator_installed
    }
  then
    build_all
  fi

  if [ "$E2E_PUSH_IMAGES" = true ] && [ -n "$E2E_OPERATOR_REGISTRY" ]
  then
    push_operator_images
  fi
}

build_all() {
  echo "Building project..."
  "$SHELL" $SHELL_XTRACE "$PROJECT_PATH"/stackgres-k8s/ci/build/build.sh \
    parent-java \
    operator-framework-java \
    test-util-java \
    common-java \
    operator-java \
    restapi-java \
    cluster-controller-java \
    stream-java \
    admin-ui \
    helm-packages \
    helm-templates \
    operator-bundle \
    $(
    if [ "$E2E_BUILD_ONLY_MODULES" != true ]
    then
    cat << EOF
    operator-jvm-image \
    restapi-jvm-image \
    cluster-controller-jvm-image \
    stream-jvm-image \
    admin-ui-image \
    operator-bundle-image \
    pglambda-javascript-image
EOF
    fi
    )
  if [ -n "$E2E_EXTRA_MOUNT_BUILD_PATH" ]
  then
    "$SHELL" $SHELL_XTRACE "$PROJECT_PATH"/stackgres-k8s/ci/build/build-functions.sh extract_all \
      parent-java \
      operator-framework-java \
      test-util-java \
      common-java \
      operator-java \
      restapi-java \
      cluster-controller-java \
      stream-java \
      admin-ui \
      helm-packages \
      helm-templates \
      operator-bundle
    k8s_copy_to_extra_mount_build_path
  fi
  if [ "$E2E_BUILD_ONLY_MODULES" != true ]
  then
    docker inspect "$(sh stackgres-k8s/ci/build/build-functions.sh image_name operator-jvm-image)" >/dev/null 2>&1 \
      || docker pull "$(sh stackgres-k8s/ci/build/build-functions.sh image_name operator-jvm-image)"
    docker inspect "$(sh stackgres-k8s/ci/build/build-functions.sh image_name restapi-jvm-image)" >/dev/null 2>&1 \
      || docker pull "$(sh stackgres-k8s/ci/build/build-functions.sh image_name restapi-jvm-image)"
    docker inspect "$(sh stackgres-k8s/ci/build/build-functions.sh image_name cluster-controller-jvm-image)" >/dev/null 2>&1 \
      || docker pull "$(sh stackgres-k8s/ci/build/build-functions.sh image_name cluster-controller-jvm-image)"
    docker inspect "$(sh stackgres-k8s/ci/build/build-functions.sh image_name stream-jvm-image)" >/dev/null 2>&1 \
      || docker pull "$(sh stackgres-k8s/ci/build/build-functions.sh image_name stream-jvm-image)"
    docker inspect "$(sh stackgres-k8s/ci/build/build-functions.sh image_name admin-ui-image)" >/dev/null 2>&1 \
      || docker pull "$(sh stackgres-k8s/ci/build/build-functions.sh image_name admin-ui-image)"
    docker inspect "$(sh stackgres-k8s/ci/build/build-functions.sh image_name operator-bundle-image)" >/dev/null 2>&1 \
      || docker pull "$(sh stackgres-k8s/ci/build/build-functions.sh image_name operator-bundle-image)"
    docker inspect "$(sh stackgres-k8s/ci/build/build-functions.sh image_name pglambda-javascript-image)" >/dev/null 2>&1 \
      || docker pull "$(sh stackgres-k8s/ci/build/build-functions.sh image_name pglambda-javascript-image)"
    docker tag "$(sh stackgres-k8s/ci/build/build-functions.sh image_name operator-jvm-image)" "$OPERATOR_IMAGE_NAME"
    docker tag "$(sh stackgres-k8s/ci/build/build-functions.sh image_name restapi-jvm-image)" "$RESTAPI_IMAGE_NAME"
    docker tag "$(sh stackgres-k8s/ci/build/build-functions.sh image_name cluster-controller-jvm-image)" "$CLUSTER_CONTROLLER_IMAGE_NAME"
    docker tag "$(sh stackgres-k8s/ci/build/build-functions.sh image_name stream-jvm-image)" "$STREAM_IMAGE_NAME"
    docker tag "$(sh stackgres-k8s/ci/build/build-functions.sh image_name admin-ui-image)" "$ADMINUI_IMAGE_NAME"
    docker tag "$(sh stackgres-k8s/ci/build/build-functions.sh image_name pglambda-javascript-image)" "$PGLAMBDA_JAVASCRIPT_IMAGE_NAME"
  fi
}

create_operator_certificate() {
  kubectl delete csr --ignore-not-found 'stackgres-operator'
  rm -f \
    "$TARGET_PATH/csr.conf" \
    "$TARGET_PATH/operator.key" \
    "$TARGET_PATH/operator.pub" \
    "$TARGET_PATH/server.csr" \
    "$TARGET_PATH/server.crt"
  cat << EOF > "$TARGET_PATH/csr.conf"
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = stackgres-operator
DNS.2 = stackgres-operator.$OPERATOR_NAMESPACE
DNS.3 = stackgres-operator.$OPERATOR_NAMESPACE.svc
DNS.4 = stackgres-operator.$OPERATOR_NAMESPACE.svc.cluster.local
EOF

  if [ "$(echo "$K8S_VERSION" | tr . '\n' \
      | while read NUMBER; do printf '%05d' "$NUMBER"; done)" \
    -ge "$(echo "1.22.0" | tr . '\n' \
      | while read NUMBER; do printf '%05d' "$NUMBER"; done)" ]
  then
    openssl req -new -nodes -text -keyout "$TARGET_PATH/operator.key" \
        -subj "/CN=system:node:stackgres-operator.$OPERATOR_NAMESPACE;/O=system:nodes" \
        -out "$TARGET_PATH/server.csr" \
        -config "$TARGET_PATH/csr.conf"
  else
    openssl req -new -nodes -text -keyout "$TARGET_PATH/operator.key" \
        -subj "/CN=stackgres-operator.$OPERATOR_NAMESPACE" \
        -out "$TARGET_PATH/server.csr" \
        -config "$TARGET_PATH/csr.conf"
  fi
  openssl rsa -in "$TARGET_PATH/operator.key" -pubout -out "$TARGET_PATH/operator.pub"

  cat << EOF | kubectl create -f -
$(
if [ "$(echo "$K8S_VERSION" | tr . '\n' \
    | while read NUMBER; do printf '%05d' "$NUMBER"; done)" \
  -ge "$(echo "1.22.0" | tr . '\n' \
    | while read NUMBER; do printf '%05d' "$NUMBER"; done)" ]
then
  echo "apiVersion: certificates.k8s.io/v1"
else
  echo "apiVersion: certificates.k8s.io/v1beta1"
fi
)
kind: CertificateSigningRequest
metadata:
  name: stackgres-operator
spec:
  request: "$(base64 "$TARGET_PATH/server.csr" | tr -d '\n')"
  usages:
  - digital signature
  - key encipherment
  - server auth
$(
if [ "$(echo "$K8S_VERSION" | tr . '\n' \
    | while read NUMBER; do printf '%05d' "$NUMBER"; done)" \
  -ge "$(echo "1.22.0" | tr . '\n' \
    | while read NUMBER; do printf '%05d' "$NUMBER"; done)" ]
then
  echo "  signerName: kubernetes.io/kubelet-serving"
fi
)
EOF

  kubectl certificate approve 'stackgres-operator'
  echo "Wait for operator certificate to be generated"
  while true
  do
    OPERATOR_CERTIFICATE="$(kubectl get csr 'stackgres-operator' -o jsonpath='{.status.certificate}')"
    if [ -n "$OPERATOR_CERTIFICATE" ]
    then
      break
    else
      echo -n .
      sleep 1
    fi
  done
  echo "done"
  printf %s "$OPERATOR_CERTIFICATE" | base64 -d > "$TARGET_PATH/operator.crt"
  kubectl delete --ignore-not-found secret stackgres-operator-certs
  cat << EOF | kubectl create -f -
apiVersion: v1
kind: Secret
metadata:
  name: stackgres-operator-certs
  namespace: $OPERATOR_NAMESPACE
type: kubernetes.io/tls
data:
  tls.key: $(base64 "$TARGET_PATH/operator.key" -w 0)
  tls.crt: $(base64 "$TARGET_PATH/operator.crt" -w 0)
EOF
}

setup_operator() {
  if [ "$E2E_SKIP_SETUP" = true ] \
    || {
      [ "$E2E_REUSE_OPERATOR_PODS" = true ] \
      && is_operator_installed
    }
  then
    if is_operator_installed
    then
      OPERATOR_NAMESPACE="$(get_operator_namespace)"
      echo "Operator already installed in namespace $OPERATOR_NAMESPACE"
    fi
    if [ "$E2E_SKIP_SETUP" = true ]
    then
      echo "Skipping operator setup"
      return
    fi
  fi

  if [ -n "$K8S_UPGRADE_TO_VERSION" ] \
    && [ -n "$K8S_UPGRADE_BEFORE_RUN_TEST" ]
  then
    E2E_BEFORE_RUN_TEST_FUNCTION="$E2E_BEFORE_RUN_TEST_FUNCTION e2e_before_run_test_upgrade_k8s"
  fi

  if [ "$E2E_SKIP_CLEANUP" != true ]
  then
    if ! is_operator_installed \
      || {
        [ "$E2E_USE_EXTERNAL_OPERATOR" != true ] \
        && [ "$E2E_REUSE_OPERATOR_PODS" != true ]
      } \
      || [ "$(get_operator_namespace)" != "$OPERATOR_NAMESPACE" ]
    then
      if [ "$E2E_USE_INTERMIDIATE_PRIVATE_REPO" != true ] \
          && [ "$E2E_SKIP_OPERATOR_LOAD" != true ]
      then
        load_operator_k8s
      fi

      echo "Performing full cleanup"
      k8s_webhook_cleanup
      helm_cleanup
      k8s_async_cleanup
      echo "Full cleanup completed"

      if [ "$E2E_SKIP_OPERATOR_INSTALL" != true ]
      then
        install_operator
        store_operator_values
      fi
    else
      echo "Performing cleanup skipping operator"
      k8s_webhook_cleanup_but_operator
      helm_cleanup_but_operator
      k8s_async_cleanup_but_operator
      echo "Cleanup skipping operator completed"
    fi
  fi
}

is_operator_installed() {
  helm list -A | tail -n +2 | tr -s '\t ' ' ' \
    | grep -q '^stackgres-operator stackgres-[^ ]\+ '
}

get_operator_namespace() {
  helm list -A | tail -n +2 | tr -s '\t ' ' ' \
    | grep '^stackgres-operator stackgres-[^ ]\+ ' | cut -d ' ' -f 2
}

load_operator_k8s() {
  local IMAGE_NAME
  if [ "$E2E_FORCE_IMAGE_PULL" = "true" ]
  then
    if [ "$E2E_SKIP_LOAD_OPERATOR" != true ]
    then
      echo "Loading operator images from $E2E_OPERATOR_REGISTRY$E2E_OPERATOR_REGISTRY_PATH"
      load_operator_images_from "$E2E_OPERATOR_REGISTRY" "$E2E_OPERATOR_REGISTRY_PATH"
      echo "All operator images loaded from $E2E_OPERATOR_REGISTRY$E2E_OPERATOR_REGISTRY_PATH"
    fi

    if [ "$E2E_SKIP_LOAD_COMPONENTS" != true ]
    then
      echo "Loading component images from ${E2E_COMPONENTS_REGISTRY}${E2E_COMPONENTS_REGISTRY_PATH}"
      load_component_images_from "$E2E_COMPONENTS_REGISTRY" "$E2E_COMPONENTS_REGISTRY_PATH"
      echo "All component images loaded from ${E2E_COMPONENTS_REGISTRY}${E2E_COMPONENTS_REGISTRY_PATH}"
    fi

    if [ "$E2E_SKIP_LOAD_EXTENSIONS" != true ]
    then
      echo "Loading extensions images from ${E2E_EXTENSIONS_REGISTRY}${E2E_EXTENSIONS_REGISTRY_PATH}"
      load_extensions_images_from "$E2E_EXTENSIONS_REGISTRY" "$E2E_EXTENSIONS_REGISTRY_PATH"
      echo "All extensions images loaded from ${E2E_EXTENSIONS_REGISTRY}${E2E_EXTENSIONS_REGISTRY_PATH}"
    fi
  fi

  if [ -n "$1" ]
  then
    local ORIGINAL_OPERATOR_IMAGE_NAME="$OPERATOR_IMAGE_NAME"
    local ORIGINAL_RESTAPI_IMAGE_NAME="$RESTAPI_IMAGE_NAME"
    local ORIGINAL_CLUSTER_CONTROLLER_IMAGE_NAME="$CLUSTER_CONTROLLER_IMAGE_NAME"
    local ORIGINAL_STREAM_IMAGE_NAME="$STREAM_IMAGE_NAME"
    local ORIGINAL_ADMINUI_IMAGE_NAME="$ADMINUI_IMAGE_NAME"
    local ORIGINAL_PGLAMBDA_JAVASCRIPT_IMAGE_NAME="$PGLAMBDA_JAVASCRIPT_IMAGE_NAME"
    local IMAGE_TAG="$1"
    local ADMINUI_IMAGE_TAG="${IMAGE_TAG%-jvm}"
    local OPERATOR_IMAGE_NAME="${OPERATOR_IMAGE_NAME%:*}:$IMAGE_TAG"
    local RESTAPI_IMAGE_NAME="${RESTAPI_IMAGE_NAME%:*}:$IMAGE_TAG"
    local CLUSTER_CONTROLLER_IMAGE_NAME="${CLUSTER_CONTROLLER_IMAGE_NAME%:*}:$IMAGE_TAG"
    local DISTRIBUTEDLOGS_CONTROLLER_IMAGE_NAME="${DISTRIBUTEDLOGS_CONTROLLER_IMAGE_NAME%:*}:$IMAGE_TAG"
    local STREAM_IMAGE_NAME="${STREAM_IMAGE_NAME%:*}:$IMAGE_TAG"
    local ADMINUI_IMAGE_NAME="${ADMINUI_IMAGE_NAME%:*}:$ADMINUI_IMAGE_TAG"
    local PGLAMBDA_JAVASCRIPT_IMAGE_NAME="${PGLAMBDA_JAVASCRIPT_IMAGE_NAME%:*}:${NATIVE_IMAGE_TAG}-javascript"
    docker_tag "$ORIGINAL_OPERATOR_IMAGE_NAME" "$OPERATOR_IMAGE_NAME"
    docker_tag "$ORIGINAL_RESTAPI_IMAGE_NAME" "$RESTAPI_IMAGE_NAME"
    docker_tag "$ORIGINAL_CLUSTER_CONTROLLER_IMAGE_NAME" "$CLUSTER_CONTROLLER_IMAGE_NAME"
    docker_tag "$ORIGINAL_STREAM_IMAGE_NAME" "$STREAM_IMAGE_NAME"
    docker_tag "$ORIGINAL_ADMINUI_IMAGE_NAME" "$ADMINUI_IMAGE_NAME"
    docker_tag "$ORIGINAL_PGLAMBDA_JAVASCRIPT_IMAGE_NAME" "$PGLAMBDA_JAVASCRIPT_IMAGE_NAME"
    load_operator_k8s_only
  else
    load_operator_k8s_only
  fi
}

load_operator_k8s_only() {
  echo "Loading operator images into Kubernetes registry"
  local IMAGE_NAME
  for IMAGE_NAME in \
    "$OPERATOR_IMAGE_NAME" \
    "$RESTAPI_IMAGE_NAME" \
    "$CLUSTER_CONTROLLER_IMAGE_NAME" \
    "$STREAM_IMAGE_NAME" \
    "$ADMINUI_IMAGE_NAME" \
    "$PGLAMBDA_JAVASCRIPT_IMAGE_NAME"
  do
    echo "$IMAGE_NAME"
  done | xargs_parallel_shell % "$E2E_PATH/e2e" load_image_k8s '%'
  echo "All operator images loaded into Kubernetes registry"
}

pull_image_from() {
  [ -n "$1" ] && [ -n "$2" ] && [ -n "$3" ]
  local REPOSITORY="$1"
  local IMAGE_PATH="$2"
  local IMAGE="$3"
  local FROM_IMAGE="$REPOSITORY${IMAGE_PATH%/}/${IMAGE##*/}"
  echo "Pulling $IMAGE from $FROM_IMAGE..."
  docker_pull "$FROM_IMAGE"
  docker_tag "$FROM_IMAGE" "$IMAGE"
  echo "Pulled $IMAGE from $FROM_IMAGE"
}

push_operator_images() {
  echo "Pushing operator images to $E2E_OPERATOR_REGISTRY$E2E_OPERATOR_REGISTRY_PATH"
  for IMAGE_NAME in \
    "$OPERATOR_IMAGE_NAME" \
    "$RESTAPI_IMAGE_NAME" \
    "$CLUSTER_CONTROLLER_IMAGE_NAME" \
    "$STREAM_IMAGE_NAME" \
    "$ADMINUI_IMAGE_NAME" \
    "$PGLAMBDA_JAVASCRIPT_IMAGE_NAME"
  do
    echo "$IMAGE_NAME"
  done | xargs_shell % "$E2E_PATH/e2e" \
    push_image_to "$E2E_OPERATOR_REGISTRY" "$E2E_OPERATOR_REGISTRY_PATH" "%"
  echo "All operator images pushed to $E2E_OPERATOR_REGISTRY$E2E_OPERATOR_REGISTRY_PATH"
}

push_image_to() {
  [ -n "$1" ] && [ -n "$2" ] && [ -n "$3" ]
  local REPOSITORY="$1"
  local IMAGE_PATH="$2"
  local IMAGE="$3"
  local TO_IMAGE="$REPOSITORY${IMAGE_PATH%/}${IMAGE#*/}"
  echo "Pushing $IMAGE to $TO_IMAGE..."
  docker_local_tag "$IMAGE" "$TO_IMAGE"
  docker_push "$TO_IMAGE"
  echo "Pushed $IMAGE to $TO_IMAGE"
}

install_operator() {
  kubectl get namespace "$OPERATOR_NAMESPACE" > /dev/null 2>&1 \
    || kubectl create namespace "$OPERATOR_NAMESPACE"

  if [ "$E2E_SKIP_UPGRADE_FROM_PREVIOUS_OPERATOR" != "true" ] \
    && [ -n "$STACKGRES_PREVIOUS_VERSION" ] \
    && can_install_operator_version "$STACKGRES_PREVIOUS_VERSION"
  then
    echo "Installing operator version $STACKGRES_PREVIOUS_VERSION"
    install_operator_previous_version "$@"
  fi

  if [ "$E2E_SKIP_UPGRADE_FROM_PREVIOUS_OPERATOR" != "true" ] \
    && [ -n "$STACKGRES_PREVIOUS_VERSION" ] \
    && [ -n "$E2E_UPGRADE_BEFORE_RUN_TEST" ] \
    && can_install_operator_version "$STACKGRES_PREVIOUS_VERSION"
  then
    # shellcheck disable=SC2034
    E2E_BEFORE_RUN_TEST_FUNCTION="$E2E_BEFORE_RUN_TEST_FUNCTION e2e_before_run_test_upgrade_operator"
  else
    if [ "$E2E_SKIP_UPGRADE_FROM_PREVIOUS_OPERATOR" != "true" ] \
      && [ -n "$STACKGRES_PREVIOUS_VERSION" ] \
      && can_install_operator_version "$STACKGRES_PREVIOUS_VERSION"
    then
      echo "Upgrading operator from version $(get_installed_operator_version) to version $STACKGRES_VERSION"
    else
      echo "Installing operator version $STACKGRES_VERSION"
    fi
    install_operator_only "$@"
  fi

  if [ -n "$K8S_UPGRADE_TO_VERSION" ] \
      && ! [ -n "$K8S_UPGRADE_BEFORE_RUN_TEST" ]
  then
    echo "Upgrading kubernetes from version $K8S_VERSION to version $K8S_UPGRADE_TO_VERSION"
    upgrade_kubernetes "$K8S_UPGRADE_TO_VERSION"
  fi
}

e2e_before_run_test_upgrade_operator() {
  if [ "$E2E_UPGRADE_BEFORE_RUN_TEST" = "$E2E_BEFORE_RUN_TEST_COUNT" ]
  then
    echo "Upgrading operator from version $(get_installed_operator_version) to version $STACKGRES_VERSION"
    install_operator_only
  fi
}

e2e_before_run_test_upgrade_k8s() {
  if [ "$K8S_UPGRADE_BEFORE_RUN_TEST" = "$E2E_BEFORE_RUN_TEST_COUNT" ]
  then
    echo "Upgrading kubernetes from version $K8S_VERSION to version $K8S_UPGRADE_TO_VERSION"
    upgrade_kubernetes "$K8S_UPGRADE_TO_VERSION"
  fi
}

get_installed_operator_version() {
  kubectl get sgconfig -n "$OPERATOR_NAMESPACE" stackgres-operator -o name >/dev/null 2>&1 \
    && kubectl get sgconfig -n "$OPERATOR_NAMESPACE" stackgres-operator -o json \
      | jq -r '.status.version | select(. != null)' | grep . \
    || helm list -n "$OPERATOR_NAMESPACE" -o json \
      | jq -r '.[] | select(.name == "stackgres-operator").app_version'
}

install_operator_only() {
  if [ "$E2E_USE_INTERMIDIATE_PRIVATE_REPO" = true ]
  then
    echo "Installing operator intermediate repo"
    set_up_private_repository
    # shellcheck disable=SC2086
    upgrade_operator_from_private_repository \
      --install \
      $E2E_OPERATOR_OPTS "$@"
    return
  else
    echo "Installing operator vanilla"
    # shellcheck disable=SC2086
    upgrade_operator_vanilla \
    --install \
    --set-string developer.version="$STACKGRES_VERSION" \
    --set-string operator.image.name="${OPERATOR_IMAGE_NAME%:*}" --set-string operator.image.tag="${OPERATOR_IMAGE_NAME#*:}" \
    --set-string "operator.image.pullPolicy=$E2E_OPERATOR_PULL_POLICY" \
    --set-string restapi.image.name="${RESTAPI_IMAGE_NAME%:*}" --set-string restapi.image.tag="${RESTAPI_IMAGE_NAME#*:}" \
    --set-string "restapi.image.pullPolicy=$E2E_OPERATOR_PULL_POLICY" \
    --set-string adminui.image.name="${ADMINUI_IMAGE_NAME%:*}" --set-string adminui.image.tag="${ADMINUI_IMAGE_NAME#*:}" \
    --set-string "adminui.image.pullPolicy=$E2E_OPERATOR_PULL_POLICY" \
    --set-string "extensions.repositoryUrls[0]=$EXTENSIONS_REPOSITORY_URL" \
    --set extensions.cache.enabled="$(
      # shellcheck disable=SC2015
      [ "$EXTENSIONS_CACHE_ENABLED" = true ] && echo true || echo false)" \
    --set extensions.cache.preloadedExtensions="{$(
      # shellcheck disable=SC2015
      [ "$EXTENSIONS_CACHE_ENABLED" = true ] \
        && printf '%s' "$EXTENSIONS_CACHE_PRELOADED_EXTENSIONS" | jq -r 'join(",")' | tr -d '\n' \
        || true)}" \
    --set extensions.cache.hostPath="$(
      # shellcheck disable=SC2015
      [ "$EXTENSIONS_CACHE_ENABLED" = true ] && [ -n "$EXTENSIONS_CACHE_HOST_PATH" ] \
        && printf '%s' "$EXTENSIONS_CACHE_HOST_PATH" \
        || printf null)" \
    $(set_helm_values_for_developer) \
    $E2E_OPERATOR_OPTS "$@"
  fi
}

can_install_operator_version() {
  local VERSION="${1:-$STACKGRES_PREVIOUS_VERSION}"
  local VERSION_AS_NUMBER VERSION_0_9_5 VERSION_1_1_0_RC1 VERSION_1_2_0_RC1 VERSION_1_3_0_RC1
  VERSION_AS_NUMBER="$(get_version_as_number "$VERSION")"
  VERSION_0_9_5="$(get_version_as_number 0.9.5)"
  VERSION_1_1_0_RC1="$(get_version_as_number 1.1.0-RC1)"
  VERSION_1_2_0_RC1="$(get_version_as_number 1.2.0-RC1)"
  VERSION_1_3_0_RC1="$(get_version_as_number 1.3.0-RC1)"
  VERSION_1_4_0_RC1="$(get_version_as_number 1.4.0-RC1)"
  VERSION_1_5_0_RC1="$(get_version_as_number 1.5.0-RC1)"
  VERSION_1_6_0_RC1="$(get_version_as_number 1.6.0-RC1)"
  if [ "$VERSION_AS_NUMBER" -lt "$VERSION_0_9_5" ]
  then
    if {
        [ "$KUBERNETES_VERSION_NUMBER" -gt 117 ] \
        || [ "$KUBERNETES_VERSION_NUMBER" -lt 111 ]
      } \
      || [ "$(uname -m)" != x86_64 ]
    then
      return 1
    fi
  fi
  if [ "$VERSION_AS_NUMBER" -le "$VERSION_1_1_0_RC1" ]
  then
    if {
        [ "$KUBERNETES_VERSION_NUMBER" -gt 122 ] \
        || [ "$KUBERNETES_VERSION_NUMBER" -lt 116 ]
      } \
      || [ "$(uname -m)" != x86_64 ]
    then
      return 1
    fi
  fi
  if [ "$VERSION_AS_NUMBER" -lt "$VERSION_1_2_0_RC1" ]
  then
    if [ "$KUBERNETES_VERSION_NUMBER" -gt 123 ] \
      || [ "$KUBERNETES_VERSION_NUMBER" -lt 116 ]
    then
      return 1
    fi
  fi
  if [ "$VERSION_AS_NUMBER" -lt "$VERSION_1_3_0_RC1" ]
  then
    if [ "$KUBERNETES_VERSION_NUMBER" -gt 123 ] \
      || [ "$KUBERNETES_VERSION_NUMBER" -lt 118 ]
    then
      return 1
    fi
  fi
  if [ "$VERSION_AS_NUMBER" -lt "$VERSION_1_4_0_RC1" ]
  then
    if [ "$KUBERNETES_VERSION_NUMBER" -gt 124 ] \
      || [ "$KUBERNETES_VERSION_NUMBER" -lt 118 ]
    then
      return 1
    fi
  fi
  if [ "$VERSION_AS_NUMBER" -lt "$VERSION_1_5_0_RC1" ]
  then
    if [ "$KUBERNETES_VERSION_NUMBER" -gt 125 ] \
      || [ "$KUBERNETES_VERSION_NUMBER" -lt 118 ]
    then
      return 1
    fi
  fi
  if [ "$VERSION_AS_NUMBER" -lt "$VERSION_1_6_0_RC1" ]
  then
    if [ "$KUBERNETES_VERSION_NUMBER" -gt 127 ] \
      || [ "$KUBERNETES_VERSION_NUMBER" -lt 118 ]
    then
      return 1
    fi
  fi
  return
}

install_operator_previous_version() {
  kubectl get namespace "$OPERATOR_NAMESPACE" > /dev/null 2>&1 \
    || kubectl create namespace "$OPERATOR_NAMESPACE"

  # shellcheck disable=SC2086
  install_operator_vanilla_from "$OPERATOR_PREVIOUS_VERSION_HELM_URL" \
    $E2E_OPERATOR_OPTS "$@"
}

install_operator_vanilla() {
  install_operator_vanilla_from "$OPERATOR_VERSION_HELM_URL" \
    "$@"
}

install_operator_vanilla_from() {
  local OPERATOR_FROM=true
  before_operator_install_or_upgrade
  helm install stackgres-operator --namespace "$OPERATOR_NAMESPACE" \
    $(
      if [ "x$E2E_ALLOWED_NAMESPACES" != x ]
      then
        printf ' --set allowedNamespaces={%s}' "$(printf %s "$E2E_ALLOWED_NAMESPACES" | tr ' ' ',')"
      fi
      if [ "x$E2E_DISABLE_CLUSTER_ROLE" = xtrue ]
      then
        printf ' --set disableClusterRole=true'
      fi
      if [ "x$E2E_ALLOW_IMPERSONATION_FOR_REST_API" = xtrue ]
      then
        printf ' --set allowImpersonationForRestApi=true'
      fi
    ) \
    "$@"
  after_operator_install_or_upgrade
}

upgrade_operator() {
  if [ "$E2E_USE_INTERMIDIATE_PRIVATE_REPO" = true ]
  then
    set_up_private_repository
    # shellcheck disable=SC2086
    upgrade_operator_from_private_repository \
      --reuse-values \
      $E2E_OPERATOR_OPTS "$@"
    return
  fi

  # shellcheck disable=SC2086
  upgrade_operator_vanilla \
    --set-string developer.version="$STACKGRES_VERSION" \
    --set-string operator.image.name="${OPERATOR_IMAGE_NAME%:*}" --set-string operator.image.tag="${OPERATOR_IMAGE_NAME#*:}" \
    --set-string "operator.image.pullPolicy=$E2E_OPERATOR_PULL_POLICY" \
    --set-string restapi.image.name="${RESTAPI_IMAGE_NAME%:*}" --set-string restapi.image.tag="${RESTAPI_IMAGE_NAME#*:}" \
    --set-string "restapi.image.pullPolicy=$E2E_OPERATOR_PULL_POLICY" \
    --set-string adminui.image.name="${ADMINUI_IMAGE_NAME%:*}" --set-string adminui.image.tag="${ADMINUI_IMAGE_NAME#*:}" \
    --set-string "adminui.image.pullPolicy=$E2E_OPERATOR_PULL_POLICY" \
    --set-string "extensions.repositoryUrls[0]=$EXTENSIONS_REPOSITORY_URL" \
    --set extensions.cache.enabled="$(
      # shellcheck disable=SC2015
      [ "$EXTENSIONS_CACHE_ENABLED" = true ] && echo true || echo false)" \
    --set extensions.cache.preloadedExtensions="{$(
      # shellcheck disable=SC2015
      [ "$EXTENSIONS_CACHE_ENABLED" = true ] \
        && printf '%s' "$EXTENSIONS_CACHE_PRELOADED_EXTENSIONS" | jq -r 'join(",")' | tr -d '\n' \
        || true)}" \
    --set extensions.cache.hostPath="$(
      # shellcheck disable=SC2015
      [ "$EXTENSIONS_CACHE_ENABLED" = true ] && [ -n "$EXTENSIONS_CACHE_HOST_PATH" ] \
        && printf '%s' "$EXTENSIONS_CACHE_HOST_PATH" \
        || printf null)" \
    --reuse-values \
    $(set_helm_values_for_developer) \
    $E2E_OPERATOR_OPTS "$@"
}

upgrade_operator_vanilla() {
  before_operator_install_or_upgrade
  if [ "$E2E_USE_LOCAL_OPERATOR" = true ]
  then
    create_operator_certificate
    run_local_operator
    helm upgrade stackgres-operator --namespace "$OPERATOR_NAMESPACE" "$OPERATOR_VERSION_HELM_URL" \
      --set deploy.operator=false \
      --set cert.createForOperator=false \
      --set developer.externalOperatorIp="$E2E_LOCAL_OPERATOR_IP" \
      --set developer.externalOperatorPort="$E2E_LOCAL_OPERATOR_SSL_PORT" \
      $(
        if [ "x$E2E_ALLOWED_NAMESPACES" != x ]
        then
          printf ' --set allowedNamespaces={%s}' "$(printf %s "$E2E_ALLOWED_NAMESPACES" | tr ' ' ',')"
        fi
        if [ "x$E2E_DISABLE_CLUSTER_ROLE" = xtrue ]
        then
          printf ' --set disableClusterRole=true'
        fi
        if [ "x$E2E_ALLOW_IMPERSONATION_FOR_REST_API" = xtrue ]
        then
          printf ' --set allowImpersonationForRestApi=true'
        fi
      ) \
      "$@"
  else
    helm upgrade stackgres-operator --namespace "$OPERATOR_NAMESPACE" "$OPERATOR_VERSION_HELM_URL" \
      $(
        if [ "x$E2E_ALLOWED_NAMESPACES" != x ]
        then
          printf ' --set allowedNamespaces={%s}' "$(printf %s "$E2E_ALLOWED_NAMESPACES" | tr ' ' ',')"
        fi
        if [ "x$E2E_DISABLE_CLUSTER_ROLE" = xtrue ]
        then
          printf ' --set disableClusterRole=true'
        fi
        if [ "x$E2E_ALLOW_IMPERSONATION_FOR_REST_API" = xtrue ]
        then
          printf ' --set allowImpersonationForRestApi=true'
        fi
      ) \
      "$@"
  fi
  after_operator_install_or_upgrade
}

run_local_operator() {
  if kill -0 "$(cat "$TARGET_PATH/operator.pid")" > /dev/null 2>&1
  then
    kill "$(cat "$TARGET_PATH/operator.pid")"
    while kill -0 "$(cat "$TARGET_PATH/operator.pid")" > /dev/null 2>&1
    do
      sleep 2
    done
  fi
  stackgres-k8s/src/mvnw \
    -f stackgres-k8s/src/pom.xml \
    $([ "$E2E_LOCAL_OPERATOR_CLEAN" = true ] && printf clean || true) \
    install \
    -DskipTests \
    -Denforcer.skip=true \
    -pl .,operator-framework,test-util,common,operator
  stackgres-k8s/src/mvnw \
    -f stackgres-k8s/src/pom.xml \
    quarkus:dev \
    -DskipTests \
    -Denforcer.skip=true \
    -pl operator \
    -Ddebug=8000 \
    -Djvm.args="$(cat << EOF | tr '\n' ' '
        -Dquarkus.arc.dev-mode.monitoring-enabled=false
        -Dquarkus.http.host=0.0.0.0
        -Dquarkus.http.ssl.certificate.files=$TARGET_PATH/operator.crt
        -Dquarkus.http.ssl.certificate.key-files=$TARGET_PATH/operator.key
        -Dquarkus.http.port=$E2E_LOCAL_OPERATOR_PORT
        -Dquarkus.http.ssl-port=$E2E_LOCAL_OPERATOR_SSL_PORT
        -Dkubernetes.impersonate.username=system:serviceaccount:$OPERATOR_NAMESPACE:stackgres-operator
        -Dkubernetes.impersonate.group=system:serviceaccount:$OPERATOR_NAMESPACE:stackgres-operator
EOF
    )" > "$TARGET_PATH/operator.log" &
  echo "$!" > "$TARGET_PATH/operator.pid"
}

upgrade_operator_from_private_repository() {
  PRIVATE_REPO_URI=$(get_private_repository_uri)
  export PRIVATE_REPO_URI
  export PRIVATE_OPERATOR_IMAGE_NAME="$PRIVATE_REPO_URI/stackgres/operator"
  export PRIVATE_RESTAPI_IMAGE_NAME="$PRIVATE_REPO_URI/stackgres/restapi"
  export PRIVATE_CLUSTER_CONTROLLER_IMAGE_NAME="$PRIVATE_REPO_URI/stackgres/cluster-controller"
  export PRIVATE_STREAM_IMAGE_NAME="$PRIVATE_REPO_URI/stackgres/stream"
  export PRIVATE_ADMINUI_IMAGE_NAME="$PRIVATE_REPO_URI/stackgres/admin-ui"
  upgrade_operator_vanilla \
      --set-string developer.version="$STACKGRES_VERSION" \
      --set-string operator.image.name="$PRIVATE_OPERATOR_IMAGE_NAME" --set-string operator.image.tag="$IMAGE_TAG" \
      --set-string "operator.image.pullPolicy=$E2E_OPERATOR_PULL_POLICY" \
      --set-string restapi.image.name="$PRIVATE_RESTAPI_IMAGE_NAME" --set-string restapi.image.tag="$IMAGE_TAG" \
      --set-string "restapi.image.pullPolicy=$E2E_OPERATOR_PULL_POLICY" \
      --set-string adminui.image.name="$PRIVATE_ADMINUI_IMAGE_NAME" --set-string adminui.image.tag="$ADMINUI_IMAGE_TAG" \
      --set-string "adminui.image.pullPolicy=$E2E_OPERATOR_PULL_POLICY" \
      --set serviceAccount.create=true \
      --set serviceAccount.repoCredentials[0]=regcred \
      --set-string "extensions.repositoryUrls[0]=$EXTENSIONS_REPOSITORY_URL" \
      --set-string developer.extraEnv.SG_IMAGE_CLUSTER_CONTROLLER="${PRIVATE_CLUSTER_CONTROLLER_IMAGE_NAME}:$IMAGE_TAG" \
      --set-string developer.extraOpts[0]='-Dquarkus.log.category."io.stackgres".level=DEBUG' \
      --set-string developer.extraOpts[1]='-Dquarkus.log.category."io.quarkus".level=INFO' \
      --set-string developer.extraOpts[2]='-Dquarkus.log.category."io.stackgres.collector".level=TRACE' \ 
      --set-string developer.extraOpts[3]='-Dquarkus.log.category."io.stackgres.dbops".level=TRACE' \
      --set-string developer.extraOpts[4]='-Dquarkus.log.category."io.stackgres.backup".level=TRACE' \
      --set-string developer.extraOpts[5]='-Dquarkus.log.category."io.stackgres.wal-g".level=INFO' \
      --set-string developer.extraOpts[6]='-Dquarkus.log.category."io.stackgres.patroni".level=TRACE' \
      --set-string developer.extraOpts[7]='-Dquarkus.log.category."io.stackgres.fluent-bit".level=TRACE' \
      --set-string developer.extraOpts[8]='-Dquarkus.log.category."io.stackgres.fluentd".level=TRACE' \
      --set-string developer.extraOpts[9]='-Dquarkus.log.category."io.stackgres.prometheus-postgres-exporter".level=TRACE' \
      --set-string developer.extraOpts[10]='-Dquarkus.log.category."okhttp3.logging.HttpLoggingInterceptor".level='"$(
        # shellcheck disable=SC2015
        [ "$E2E_LOG_OPERATOR_HTTP" = true ] && echo TRACE || echo INFO)" \
      --set-string developer.extraOpts[11]='-Dquarkus.log.category."stackgres-extensions-cache".level=DEBUG' \
      --set-string developer.extraOpts[12]='-Dquarkus.log.category."io.stackgres.operator.conciliation".level=TRACE' \
      --set extensions.cache.enabled="$(
        # shellcheck disable=SC2015
        [ "$EXTENSIONS_CACHE_ENABLED" = true ] && echo true || echo false)" \
      --set extensions.cache.preloadedExtensions="{$(
        # shellcheck disable=SC2015
        [ "$EXTENSIONS_CACHE_ENABLED" = true ] \
          && printf '%s' "$EXTENSIONS_CACHE_PRELOADED_EXTENSIONS" | jq -r 'join(",")' | tr -d '\n' \
          || true)}" \
      --set extensions.cache.hostPath="$(
        # shellcheck disable=SC2015
        [ "$EXTENSIONS_CACHE_ENABLED" = true ] && [ -n "$EXTENSIONS_CACHE_HOST_PATH" ] \
          && printf '%s' "$EXTENSIONS_CACHE_HOST_PATH" \
          || printf null)" \
      "$@"
}

before_operator_install_or_upgrade() {
  if [ "x$E2E_ALLOWED_NAMESPACES" != x ]
  then
    printf %s "$E2E_ALLOWED_NAMESPACES" | tr ' ' '\n' | xargs -I @ALLOWED_NAMESPACE "$SHELL" $SHELL_XTRACE -c \
      "kubectl create ns '@ALLOWED_NAMESPACE' 2>/dev/null || kubectl get ns '@ALLOWED_NAMESPACE'" >/dev/null
    K8S_EXCLUDED_NAMESPACES="$K8S_EXCLUDED_NAMESPACES $E2E_ALLOWED_NAMESPACES"
  fi
  setup_extensions_secrets
}

after_operator_install_or_upgrade() {
  kubectl label namespace "$OPERATOR_NAMESPACE" --overwrite monitoring=true
  wait_services_available "$OPERATOR_NAMESPACE" 1 '^stackgres-operator$'
  wait_until eval 'kubectl get validatingwebhookconfigurations.admissionregistration.k8s.io stackgres-operator -o json \
    | jq ".webhooks|any(.clientConfig.caBundle == null)" \
    | grep -qxF false'
  wait_until eval 'kubectl get mutatingwebhookconfigurations.admissionregistration.k8s.io stackgres-operator -o json \
    | jq ".webhooks|any(.clientConfig.caBundle == null)" \
    | grep -qxF false'
  wait_services_available "$OPERATOR_NAMESPACE" 1 '^stackgres-restapi$'
  wait_until eval 'kubectl get job -n "$OPERATOR_NAMESPACE" -l "app=StackGresConfig" -o name | wc -l | grep -qxF 0'
  if [ "$OPERATOR_FROM" != true ] && [ "$EXTENSIONS_CACHE_ENABLED" = true ]
  then
    wait_services_available "$OPERATOR_NAMESPACE" 1 '^stackgres-operator-extensions-cache$'
  fi
  set_max_length
  update_ui_clear_password
}

update_ui_clear_password() {
  local PASSWORD
  PASSWORD="$(helm get values -n "$OPERATOR_NAMESPACE" stackgres-operator -o json \
    | jq -r '.authentication.password | select(. != null)' | tr -d '\n')"
  if [ -n "$PASSWORD" ]
  then
    local OPERATOR_VERSION
    OPERATOR_VERSION="$(get_installed_operator_version)"
    if [ -n "$OPERATOR_VERSION" ] && [ "$OPERATOR_VERSION" != "$STACKGRES_VERSION" ]
    then
      local VERSION_AS_NUMBER VERSION_1_6_0_BETA_1_AS_NUMBER
      VERSION_AS_NUMBER="$(get_version_as_number "$OPERATOR_VERSION")"
      VERSION_1_6_0_BETA_1_AS_NUMBER="$(get_version_as_number 1.6.0-beta1)"
      if [ "$VERSION_AS_NUMBER" -lt "$VERSION_1_6_0_BETA_1_AS_NUMBER" ]
      then
        kubectl patch secret -n "$OPERATOR_NAMESPACE" stackgres-restapi -p '{"data":{"clearPassword":"'"$(printf '%s' "$PASSWORD" | base64)"'"}}'
        return
      fi
    fi
    kubectl patch secret -n "$OPERATOR_NAMESPACE" stackgres-restapi-admin -p '{"data":{"clearPassword":"'"$(printf '%s' "$PASSWORD" | base64)"'"}}'
  fi
}

delete_operator() {
  delete_operator_only "$@"
  k8s_cleanup_namespace "$OPERATOR_NAMESPACE"
}

delete_operator_only() {
  ! helm get notes stackgres-operator --namespace "$OPERATOR_NAMESPACE" > /dev/null \
    || helm uninstall stackgres-operator --namespace "$OPERATOR_NAMESPACE" "$@" \
    || true
}

get_installed_operator_values() {
  helm get values --namespace "$OPERATOR_NAMESPACE" stackgres-operator -o json
}

store_operator_values() {
  get_installed_operator_values > "$TARGET_PATH/operator-values.json"
}

reinstall_operator_if_not_as_expected() {
  if [ "$E2E_SKIP_OPERATOR_INSTALL" = true ]
  then
    return
  fi
  if ! helm get notes stackgres-operator --namespace "$OPERATOR_NAMESPACE" > /dev/null 2>&1 \
    || {
      [ "$(get_installed_operator_values)" != "$(cat "$TARGET_PATH/operator-values.json")" ] \
      && [ "$E2E_FORCE_REUSE_OPERATOR" != true ]
    }
  then
    if ! helm get notes stackgres-operator --namespace "$OPERATOR_NAMESPACE" > /dev/null 2>&1
    then
      echo "Re-installing operator since not installed"
    else
      echo "Re-installing operator since has different values"
      echo
      if [ -f "$TARGET_PATH/operator-values.json" ]
      then
        echo "Values expected:"
        cat "$TARGET_PATH/operator-values.json"
      fi
      echo
      echo "Values found:"
      get_installed_operator_values || true
      echo
    fi
    k8s_webhook_cleanup
    helm_cleanup
    k8s_cleanup

    kubectl create namespace "$OPERATOR_NAMESPACE"
    if [ -f "$TARGET_PATH/operator-values.json" ]
    then
      install_operator_only -f "$TARGET_PATH/operator-values.json"
      store_operator_values
    else
      install_operator_only
      store_operator_values
    fi
  fi
}

load_operator_images_from() {
  local REPOSITORY="${1:-$OPERATOR_PUBLIC_REGISTRY}"
  local IMAGE_PATH="${2:-$OPERATOR_PUBLIC_REGISTRY_PATH}"
  local VERSION="${3:-$STACKGRES_VERSION}"
  local IMAGES
  IMAGES="$(get_operator_images "$VERSION")"
  printf '%s' "$IMAGES" \
    | xargs_parallel_shell % "$E2E_PATH/e2e" \
      pull_image_from "$REPOSITORY" "${IMAGE_PATH%/}/stackgres" "%"
}

get_operator_images() {
  [ -n "$1" ]
  local VERSION="$1"
  local VERSION_AS_NUMBER VERSION_1_0_0_ALPHA1 VERSION_1_0_0_ALPHA2 VERSION_1_12_0_ALPHA1 VERSION_1_13_0_ALPHA1 VERSION_1_14_0_ALPHA1 VERSION_1_18_0_ALPHA1
  VERSION_AS_NUMBER="$(get_version_as_number "$VERSION")"
  VERSION_1_0_0_ALPHA1="$(get_version_as_number 1.0.0-alpha1)"
  VERSION_1_0_0_ALPHA2="$(get_version_as_number 1.0.0-alpha2)"
  VERSION_1_12_0_ALPHA1="$(get_version_as_number 1.12.0-alpha1)"
  VERSION_1_13_0_ALPHA1="$(get_version_as_number 1.13.0-alpha1)"
  VERSION_1_14_0_ALPHA1="$(get_version_as_number 1.14.0-alpha1)"
  VERSION_1_18_0_ALPHA1="$(get_version_as_number 1.18.0-alpha1)"
  if [ "$VERSION_AS_NUMBER" -lt "$VERSION_1_0_0_ALPHA1" ]
  then
    get_operator_images_pre_1_0_0_alpha1 "$VERSION"
    return
  elif [ "$VERSION_AS_NUMBER" -lt "$VERSION_1_0_0_ALPHA2" ]
  then
    get_operator_images_pre_1_0_0_alpha2 "$VERSION"
    return
  elif [ "$VERSION_AS_NUMBER" -lt "$VERSION_1_12_0_ALPHA1" ]
  then
    get_operator_images_pre_1_12_0_alpha1 "$VERSION"
    return
  elif [ "$VERSION_AS_NUMBER" -lt "$VERSION_1_13_0_ALPHA1" ]
  then
    get_operator_images_pre_1_13_0_alpha1 "$VERSION"
    return
  elif [ "$VERSION_AS_NUMBER" -lt "$VERSION_1_14_0_ALPHA1" ]
  then
    get_operator_images_pre_1_14_0_alpha1 "$VERSION"
    return
  elif [ "$VERSION_AS_NUMBER" -lt "$VERSION_1_18_0_ALPHA1" ]
  then
    get_operator_images_pre_1_18_0_alpha1 "$VERSION"
    return
  fi
  local NATIVE_TAG="$VERSION"
  if [ "$VERSION" = "$STACKGRES_VERSION" ]
  then
    NATIVE_TAG="${IMAGE_TAG%-jvm}"
  fi
  local TAG="$NATIVE_TAG-jvm"
  if [ "$VERSION" = "$STACKGRES_VERSION" ] \
    && [ "${IMAGE_TAG%-jvm}" = "$IMAGE_TAG" ]
  then
    TAG="$NATIVE_TAG"
  fi
  echo "${OPERATOR_IMAGE_NAME%:*}:$TAG"
  echo "${RESTAPI_IMAGE_NAME%:*}:$TAG"
  echo "${CLUSTER_CONTROLLER_IMAGE_NAME%:*}:$TAG"
  echo "${STREAM_IMAGE_NAME%:*}:${NATIVE_TAG}-jvm"
  echo "${ADMINUI_IMAGE_NAME%:*}:$NATIVE_TAG"
  echo "${PGLAMBDA_JAVASCRIPT_IMAGE_NAME%:*}:${NATIVE_TAG}-javascript"
}

get_operator_images_pre_1_18_0_alpha1() {
  [ -n "$1" ]
  local VERSION="$1"
  echo "${OPERATOR_IMAGE_NAME%:*}:$VERSION"
  echo "${RESTAPI_IMAGE_NAME%:*}:$VERSION"
  echo "${JOBS_IMAGE_NAME%:*}:$VERSION"
  echo "${CLUSTER_CONTROLLER_IMAGE_NAME%:*}:$VERSION"
  echo "${STREAM_IMAGE_NAME%:*}:$VERSION-jvm"
  echo "${ADMINUI_IMAGE_NAME%:*}:$VERSION"
  echo "${PGLAMBDA_JAVASCRIPT_IMAGE_NAME%:*}:${NATIVE_TAG}-javascript"
}

get_operator_images_pre_1_14_0_alpha1() {
  [ -n "$1" ]
  local VERSION="$1"
  echo "${OPERATOR_IMAGE_NAME%:*}:$VERSION"
  echo "${RESTAPI_IMAGE_NAME%:*}:$VERSION"
  echo "${JOBS_IMAGE_NAME%:*}:$VERSION"
  echo "${CLUSTER_CONTROLLER_IMAGE_NAME%:*}:$VERSION"
  echo "${DISTRIBUTEDLOGS_CONTROLLER_IMAGE_NAME%:*}:$VERSION"
  echo "${STREAM_IMAGE_NAME%:*}:$VERSION-jvm"
  echo "${ADMINUI_IMAGE_NAME%:*}:$VERSION"
  echo "${PGLAMBDA_JAVASCRIPT_IMAGE_NAME%:*}:${VERSION}-javascript"
}

get_operator_images_pre_1_13_0_alpha1() {
  [ -n "$1" ]
  local VERSION="$1"
  echo "${OPERATOR_IMAGE_NAME%:*}:$VERSION"
  echo "${RESTAPI_IMAGE_NAME%:*}:$VERSION"
  echo "${JOBS_IMAGE_NAME%:*}:$VERSION"
  echo "${CLUSTER_CONTROLLER_IMAGE_NAME%:*}:$VERSION"
  echo "${DISTRIBUTEDLOGS_CONTROLLER_IMAGE_NAME%:*}:$VERSION"
  echo "${STREAM_IMAGE_NAME%:*}:$VERSION-jvm"
  echo "${ADMINUI_IMAGE_NAME%:*}:$VERSION"
}

get_operator_images_pre_1_12_0_alpha1() {
  [ -n "$1" ]
  local VERSION="$1"
  echo "${OPERATOR_IMAGE_NAME%:*}:$VERSION"
  echo "${RESTAPI_IMAGE_NAME%:*}:$VERSION"
  echo "${CLUSTER_CONTROLLER_IMAGE_NAME%:*}:$VERSION"
  echo "${DISTRIBUTEDLOGS_CONTROLLER_IMAGE_NAME%:*}:$VERSION"
  echo "${ADMINUI_IMAGE_NAME%:*}:$VERSION"
}

get_operator_images_pre_1_0_0_alpha2() {
  [ -n "$1" ]
  local VERSION="$1"
  echo "${OPERATOR_IMAGE_NAME%:*}:$VERSION"
  echo "${RESTAPI_IMAGE_NAME%:*}:$VERSION"
  echo "${JOBS_IMAGE_NAME%:*}:$VERSION"
  echo "${CLUSTER_CONTROLLER_IMAGE_NAME%:*}:$VERSION"
  echo "${DISTRIBUTEDLOGS_CONTROLLER_IMAGE_NAME%:*}:$VERSION"
  echo "${ADMINUI_IMAGE_NAME%:*}:$VERSION"
}

get_operator_images_pre_1_0_0_alpha1() {
  [ -n "$1" ]
  local VERSION="$1"
  echo "${OPERATOR_IMAGE_NAME%:*}:$VERSION"
  echo "${RESTAPI_IMAGE_NAME%:*}:$VERSION"
  echo "${ADMINUI_IMAGE_NAME%:*}:$VERSION"
}

load_component_images_from() {
  local REPOSITORY="${1:-$COMPONENT_PUBLIC_REGISTRY}"
  local IMAGE_PATH="${2:-$COMPONENT_PUBLIC_REGISTRY_PATH}"
  local VERSION="${3:-$STACKGRES_VERSION}"
  local COMPONENTS
  local INCLUDE_ONLY_POSTGRES_VERSIONS="$E2E_INCLUDE_ONLY_POSTGRES_VERSIONS"
  COMPONENTS="$(get_component_images "$VERSION")"
  if [ -n "$INCLUDE_ONLY_POSTGRES_VERSIONS" ]
  then
    INCLUDE_ONLY_POSTGRES_VERSIONS="$INCLUDE_ONLY_POSTGRES_VERSIONS $E2E_POSTGRES_VERSION $E2E_UI_POSTGRES_VERSION"
    COMPONENTS="$(
      printf '%s' "$COMPONENTS" \
        | grep "/patroni\(-ext\)\?:v[^-]\+-..\($(printf '%s' "$INCLUDE_ONLY_POSTGRES_VERSIONS" \
          | sed 's/ /\\|/g' | sed 's/\./\\./g')\)-"
      printf '%s' "$COMPONENTS" \
        | grep "/postgres-util:v\($(printf '%s' "$INCLUDE_ONLY_POSTGRES_VERSIONS" \
          | sed 's/ /\\|/g' | sed 's/\./\\./g')\)-"
      printf '%s' "$COMPONENTS" \
        | grep -v "\(/postgres-util:\|/patroni\(-ext\)\?:\)")"
  fi
  printf '%s' "$COMPONENTS" \
    | xargs_parallel_shell % "$E2E_PATH/e2e" \
      load_image_from "$REPOSITORY" "$IMAGE_PATH" '%'
}

load_extensions_images_from() {
  local REPOSITORY="$1"
  local IMAGE_PATH="$2"
  local EXTENSIONS
  EXTENSIONS="$(get_preloaded_extensions)"
  EXTENSIONS="$(
    # shellcheck disable=SC2030
    printf '%s' "$EXTENSIONS" \
      | while read -r REPOSITORY PUBLISHER NAME VERSION POSTGRES_VERSION FLAVOR BUILD_ARCH BUILD_OS BUILD
        do
          get_extension_image "$EXTENSIONS_REPOSITORY_URL" "$PUBLISHER" "$NAME" \
            "$VERSION" "$POSTGRES_VERSION" "$FLAVOR" "$BUILD_ARCH" "$BUILD_OS" "$BUILD"
        done)"
  # shellcheck disable=SC2031
  if [ -n "$REPOSITORY" ] && [ -n "$IMAGE_PATH" ]
  then
    # shellcheck disable=SC2031
    printf '%s' "$EXTENSIONS" \
      | xargs_parallel_shell % "$E2E_PATH/e2e" \
        load_image_from "$REPOSITORY" "$IMAGE_PATH" '%'
  else
    printf '%s' "$EXTENSIONS" \
      | xargs_parallel_shell % "$E2E_PATH/e2e" \
        load_image_k8s '%'
  fi
}

load_image_from() {
  [ -n "$1" ] && [ -n "$2" ] && [ -n "$3" ]
  local REPOSITORY="$1"
  local IMAGE_PATH="$2"
  local IMAGE="$3"
  pull_image_from "$REPOSITORY" "$IMAGE_PATH" "$IMAGE"
  load_image_k8s "$IMAGE"
}

get_component_images() {
  notrace_function traceable_get_component_images "$1"
}

traceable_get_component_images() {
  [ -n "$1" ]
  local VERSION="$1"
  local VERSION_AS_NUMBER VERSION_1_0_0_ALPHA1
  VERSION_AS_NUMBER="$(get_version_as_number "$VERSION")"
  VERSION_1_0_0_ALPHA1="$(get_version_as_number 1.0.0-alpha1)"
  if [ "$VERSION_AS_NUMBER" -le "$VERSION_1_0_0_ALPHA1" ]
  then
    get_component_images_pre_1_0_0_alpha2 "$VERSION"
    return
  fi
  local COMPONENT_POSTGRESQL_VERSIONS COMPONENT_BABELFISH_VERSIONS \
    COMPONENT_PATRONI_VERSIONS \
    COMPONENT_PGBOUNCER_VERSIONS COMPONENT_POSTGRES_EXPORTER_VERSIONS \
    COMPONENT_ENVOY_VERSIONS COMPONENT_FLUENTBIT_VERSIONS \
    COMPONENT_FLUENTD_VERSIONS
  COMPONENT_POSTGRESQL_VERSIONS="$(get_component_versions "$VERSION" postgresql)"
  COMPONENT_BABELFISH_VERSIONS="$(get_component_versions "$VERSION" babelfish)"
  COMPONENT_PATRONI_VERSIONS="$(get_component_versions "$VERSION" patroni)"
  COMPONENT_PGBOUNCER_VERSIONS="$(get_component_versions "$VERSION" pgbouncer)"
  COMPONENT_POSTGRES_EXPORTER_VERSIONS="$(get_component_versions "$VERSION" prometheus-postgres-exporter)"
  COMPONENT_ENVOY_VERSIONS="$(get_component_versions "$VERSION" envoy)"
  COMPONENT_FLUENTBIT_VERSIONS="$(get_component_versions "$VERSION" fluentbit)"
  COMPONENT_FLUENTD_VERSIONS="$(get_component_versions "$VERSION" fluentd)"
  COMPONENT_KUBECTL_VERSIONS="$(get_component_versions "$VERSION" kubectl)"
  COMPONENT_BABELFISH_COMPASS_VERSIONS="$(get_component_versions "$VERSION" babelfish-compass)"
  COMPONENT_OTEL_COLLECTOR_VERSIONS="$(get_component_versions "$VERSION" otel-collector)"
  local VERSION
  local SUBVERSION
  for VERSION in $COMPONENT_PATRONI_VERSIONS
  do
    for SUBVERSION in $COMPONENT_POSTGRESQL_VERSIONS
    do
      if [ "${VERSION#*-build-}" = "${SUBVERSION#*-build-}" ]
      then
        echo "quay.io/ongres/patroni:v${VERSION%-build-*}-pg${SUBVERSION%-build-*}-build-${VERSION#*-build-}"
      fi
    done
    for SUBVERSION in $COMPONENT_BABELFISH_VERSIONS
    do
      if [ "${VERSION#*-build-}" = "${SUBVERSION#*-build-}" ]
      then
        echo "quay.io/ongres/patroni:v${VERSION%-build-*}-bf${SUBVERSION%-build-*}-build-${VERSION#*-build-}"
      fi
    done
  done
  for VERSION in $COMPONENT_POSTGRESQL_VERSIONS
  do
    echo "quay.io/ongres/postgres-util:v$VERSION"
  done
  for VERSION in $COMPONENT_PGBOUNCER_VERSIONS
  do
    echo "quay.io/ongres/pgbouncer:v$VERSION"
  done
  for VERSION in $COMPONENT_POSTGRES_EXPORTER_VERSIONS
  do
    echo "quay.io/ongres/prometheus-postgres-exporter:v$VERSION"
  done
  for VERSION in $COMPONENT_ENVOY_VERSIONS
  do
    echo "quay.io/ongres/envoy:v$VERSION"
  done
  for VERSION in $COMPONENT_FLUENTBIT_VERSIONS
  do
    echo "quay.io/ongres/fluentbit:v$VERSION"
  done
  for VERSION in $COMPONENT_FLUENTD_VERSIONS
  do
    echo "quay.io/ongres/fluentd:v$VERSION"
  done
  for VERSION in $COMPONENT_KUBECTL_VERSIONS
  do
    echo "quay.io/ongres/kubectl:v$VERSION"
  done
  for VERSION in $COMPONENT_BABELFISH_COMPASS_VERSIONS
  do
    echo "quay.io/ongres/babelfish-compass:v$VERSION"
  done
  for VERSION in $COMPONENT_OTEL_COLLECTOR_VERSIONS
  do
    echo "quay.io/ongres/otel-collector:v$VERSION"
  done
}

get_pom_url() {
  [ -n "$1" ]
  echo "https://gitlab.com/ongresinc/stackgres/-/raw/$1/stackgres-k8s/src/pom.xml"
}

get_component_versions() {
  notrace_function traceable_get_component_versions "$1" "$2"
}

traceable_get_component_versions() {
  [ -n "$1" ] && [ -n "$2" ]
  local VERSION="$1"
  local COMPONENT="$2"
  (
  if [ "$STACKGRES_VERSION" = "$VERSION" ]
  then
    if [ -n "$STACKGRES_TARGET_VERSION" ] && [ "${STACKGRES_TARGET_VERSION%.*}" != "${STACKGRES_VERSION%.*}" ]
    then
      cat "$STACKGRES_PATH/src/common/src/main/resources/versions-${STACKGRES_TARGET_VERSION%.*}.properties"
    else
      cat "$STACKGRES_PATH/src/common/src/main/resources/versions-${STACKGRES_VERSION%.*}.properties"
    fi
  else
    curl -f -s "$(get_versions_url "$VERSION")"
  fi
  ) | tr -d " " | sed -e :a -e '/\\$/N; s/\\\n//; ta' \
    | grep -F "$COMPONENT=" \
    | cut -d '=' -f 2 | tr ',' '\n' \
    | sort -t ' ' -k 1V,1V
}

get_versions_url() {
  [ -n "$1" ]
  VERSION_AS_NUMBER="$(get_version_as_number "$VERSION")"
  VERSION_1_18_0RC1="$(get_version_as_number 1.18.0-rc1)"
  if [ "$VERSION_AS_NUMBER" -gt "$VERSION_1_18_0RC1" ]
  then
    echo "https://gitlab.com/ongresinc/stackgres/-/raw/$1/stackgres-k8s/src/common/src/main/resources/versions-${VERSION%.*}.properties"
  else
    echo "https://gitlab.com/ongresinc/stackgres/-/raw/$1/stackgres-k8s/src/common/src/main/resources/versions.properties"
  fi
}

get_component_images_pre_1_0_0_alpha2() {
  [ -n "$1" ]
  local VERSION="$1"
  local BUILD
  local VERSION_AS_NUMBER VERSION_0_9_4 VERSION_1_0_0_ALPHA1
  VERSION_AS_NUMBER="$(get_version_as_number "$VERSION")"
  VERSION_0_9_4="$(get_version_as_number 0.9.4)"
  VERSION_1_0_0_ALPHA1="$(get_version_as_number 1.0.0-alpha1)"
  BUILD="$(get_container_build_pre_1_0_0_alpha2 "$VERSION")"
  if [ "$VERSION_AS_NUMBER" -le "$VERSION_0_9_4" ] \
    || [ "$VERSION_AS_NUMBER" -ge "$VERSION_1_0_0_ALPHA1" ]
  then
    get_component_images_pre_0_9_5 "$BUILD"
    return
  fi
  local COMPONENT_POSTGRESQL_VERSIONS COMPONENT_PATRONI_VERSIONS \
    COMPONENT_PGBOUNCER_VERSIONS COMPONENT_POSTGRES_EXPORTER_VERSIONS \
    COMPONENT_ENVOY_VERSIONS COMPONENT_FLUENTBIT_VERSIONS \
    COMPONENT_FLUENTD_VERSIONS
  COMPONENT_POSTGRESQL_VERSIONS="$(get_component_versions_pre_1_0_0_alpha2 "$VERSION" postgresql)"
  COMPONENT_PATRONI_VERSIONS="$(get_component_versions_pre_1_0_0_alpha2 "$VERSION" patroni)"
  COMPONENT_PGBOUNCER_VERSIONS="$(get_component_versions_pre_1_0_0_alpha2 "$VERSION" pgbouncer)"
  COMPONENT_POSTGRES_EXPORTER_VERSIONS="$(get_component_versions_pre_1_0_0_alpha2 "$VERSION" prometheus-postgres-exporter)"
  COMPONENT_ENVOY_VERSIONS="$(get_component_versions_pre_1_0_0_alpha2 "$VERSION" envoy)"
  COMPONENT_FLUENTBIT_VERSIONS="$(get_component_versions_pre_1_0_0_alpha2 "$VERSION" fluentbit)"
  COMPONENT_FLUENTD_VERSIONS="$(get_component_versions_pre_1_0_0_alpha2 "$VERSION" fluentd)"
  local VERSION
  local SUBVERSION
  for VERSION in $COMPONENT_PATRONI_VERSIONS
  do
    for SUBVERSION in $COMPONENT_POSTGRESQL_VERSIONS
    do
      echo "quay.io/ongres/patroni-ext:v$VERSION-pg$SUBVERSION-build-$BUILD"
    done
  done
  for VERSION in $COMPONENT_POSTGRESQL_VERSIONS
  do
    echo "quay.io/ongres/postgres-util:v$VERSION-build-$BUILD"
  done
  for VERSION in $COMPONENT_PGBOUNCER_VERSIONS
  do
    echo "quay.io/ongres/pgbouncer:v$VERSION-build-$BUILD"
  done
  for VERSION in $COMPONENT_POSTGRES_EXPORTER_VERSIONS
  do
    echo "quay.io/ongres/prometheus-postgres-exporter:v$VERSION-build-$BUILD"
  done
  for VERSION in $COMPONENT_ENVOY_VERSIONS
  do
    echo "quay.io/ongres/envoy:v$VERSION-build-$BUILD"
  done
  for VERSION in $COMPONENT_FLUENTBIT_VERSIONS
  do
    echo "quay.io/ongres/fluentbit:v$VERSION-build-$BUILD"
  done
  for VERSION in $COMPONENT_FLUENTD_VERSIONS
  do
    echo "quay.io/ongres/fluentd:v$VERSION-build-$BUILD"
  done
}

get_container_build_pre_1_0_0_alpha2() {
  [ -n "$1" ]
  local VERSION="$1"
  (
  if [ "$STACKGRES_VERSION" = "$VERSION" ]
  then
    cat "$STACKGRES_PATH/src/pom.xml"
  else
    curl -f -s "$(get_pom_url "$VERSION")"
  fi
  ) | grep -F '<stackgres.containerBuild>' \
    | cut -d '>' -f 2 | cut -d '<' -f 1
}

get_component_versions_pre_1_0_0_alpha2() {
  [ -n "$1" ] && [ -n "$2" ]
  local VERSION="$1"
  local COMPONENT="$2"
  (
  if [ "$STACKGRES_VERSION" = "$VERSION" ]
  then
    cat "$STACKGRES_PATH/src/operator/src/main/resources/versions.properties"
  else
    curl -f -s "$(get_versions_url_pre_1_0_0_alpha2 "$VERSION")"
  fi
  ) | grep -F "$COMPONENT=" \
    | cut -d '=' -f 2 | tr ',' '\n' \
    | sort -t ' ' -k 1V,1V
}

get_versions_url_pre_1_0_0_alpha2() {
  [ -n "$1" ]
  echo "https://gitlab.com/ongresinc/stackgres/-/raw/$1/stackgres-k8s/src/operator/src/main/resources/versions.properties"
}

get_component_images_pre_0_9_5() {
  [ -n "$1" ]
  local COMPONENTS_VERSIONS_YAML
  COMPONENTS_VERSIONS_YAML="$(curl -f -s "$(get_components_versions_url_pre_0_9_5 "$1")")"
  local COMPONENT_POSTGRESQL_VERSIONS COMPONENT_PATRONI_VERSIONS \
    COMPONENT_PGBOUNCER_VERSIONS COMPONENT_POSTGRES_EXPORTER_VERSIONS \
    COMPONENT_ENVOY_VERSIONS COMPONENT_FLUENTBIT_VERSIONS \
    COMPONENT_FLUENTD_VERSIONS
  COMPONENT_POSTGRESQL_VERSIONS="$(get_component_versions_pre_0_9_5 postgresql)"
  COMPONENT_PATRONI_VERSIONS="$(get_component_versions_pre_0_9_5 patroni)"
  COMPONENT_PGBOUNCER_VERSIONS="$(get_component_versions_pre_0_9_5 pgbouncer)"
  COMPONENT_POSTGRES_EXPORTER_VERSIONS="$(get_component_versions_pre_0_9_5 postgres_exporter)"
  COMPONENT_ENVOY_VERSIONS="$(get_component_versions_pre_0_9_5 envoy)"
  COMPONENT_FLUENTBIT_VERSIONS="$(get_component_versions_pre_0_9_5 fluentbit)"
  COMPONENT_FLUENTD_VERSIONS="$(get_component_versions_pre_0_9_5 fluentd)"
  local VERSION
  local SUBVERSION
  for VERSION in $COMPONENT_PATRONI_VERSIONS
  do
    echo "$VERSION"
  done | sort -t ' ' -k 1V,1V \
    | while read -r VERSION
      do
        for SUBVERSION in $COMPONENT_POSTGRESQL_VERSIONS
        do
          echo "$SUBVERSION"
        done | sort -t ' ' -k 1V,1V \
          | while read -r SUBVERSION
            do
              echo "quay.io/ongres/patroni:v$VERSION-pg$SUBVERSION-build-$1"
            done
      done
  for VERSION in $COMPONENT_POSTGRESQL_VERSIONS
  do
    echo "$VERSION"
  done | sort -t ' ' -k 1V,1V \
    | while read -r VERSION
      do
        echo "quay.io/ongres/postgres-util:v$VERSION-build-$1"
      done
  for VERSION in $COMPONENT_PGBOUNCER_VERSIONS
  do
    echo "$VERSION"
  done | sort -t ' ' -k 1V,1V \
    | while read -r VERSION
      do
        echo "quay.io/ongres/pgbouncer:v$VERSION-build-$1"
      done
  for VERSION in $COMPONENT_POSTGRES_EXPORTER_VERSIONS
  do
    echo "$VERSION"
  done | sort -t ' ' -k 1V,1V \
    | while read -r VERSION
      do
        echo "quay.io/ongres/prometheus-postgres-exporter:v$VERSION-build-$1"
      done
  for VERSION in $COMPONENT_ENVOY_VERSIONS
  do
    echo "$VERSION"
  done | sort -t ' ' -k 1V,1V \
    | while read -r VERSION
      do
        echo "quay.io/ongres/envoy:v$VERSION-build-$1"
      done
  for VERSION in $COMPONENT_FLUENTBIT_VERSIONS
  do
    echo "$VERSION"
  done | sort -t ' ' -k 1V,1V \
    | while read -r VERSION
      do
        echo "quay.io/ongres/fluentbit:v$VERSION-build-$1"
      done
  for VERSION in $COMPONENT_FLUENTD_VERSIONS
  do
    echo "$VERSION"
  done | sort -t ' ' -k 1V,1V \
    | while read -r VERSION
      do
        echo "quay.io/ongres/fluentd:v$VERSION-build-$1"
      done
}

get_components_versions_url_pre_0_9_5() {
  [ -n "$1" ]
  echo "https://stackgres.io/downloads/stackgres-k8s/stackgres/components/$1/versions.yaml"
}

get_component_versions_pre_0_9_5() {
  local X_UNSET=true
  if echo "$-" | grep -q x
  then
    X_UNSET=false
  fi
  "$X_UNSET" || set +x
  [ -n "$COMPONENTS_VERSIONS_YAML" ]
  echo "$COMPONENTS_VERSIONS_YAML" | tr -d '\n' \
    | sed 's/.*\s\+'"$1"':\s\+versions:\(\(\s\+-\s\+"\([^"]\+\)"\)\+\).*/\1/g' \
    | tr -d '" ' | sed 's/^-//' | tr '-' '\n'
  "$X_UNSET" || set -x
}

get_operator_image_repository() {
  [ -n "$1" ]
  local VERSION="$1"

  if [ "$VERSION" = "$STACKGRES_VERSION" ] \
    &&[ "$E2E_USE_INTERMIDIATE_PRIVATE_REPO" = true ]
  then
    echo "$(get_private_repository_uri)/stackgres"
    return
  fi

  if [ "$VERSION" = "$STACKGRES_VERSION" ]
  then
    echo "${OPERATOR_IMAGE_NAME%/*}"
    return
  fi

  local VERSION_AS_NUMBER VERSION_1_4_3
  VERSION_AS_NUMBER="$(get_version_as_number "$VERSION")"
  VERSION_1_4_3="$(get_version_as_number 1.4.3)"

  if [ "$VERSION_AS_NUMBER" -le "$VERSION_1_4_3" ]
  then
    echo "docker.io/stackgres"
    return
  fi

  echo "quay.io/stackgres"
}

get_operator_image_tag() {
  [ -n "$1" ]
  local VERSION="$1"
  local VERSION_AS_NUMBER VERSION_0_9_5 VERSION_1_0_0_ALPHA1 VERSION_1_0_0_ALPHA2
  VERSION_AS_NUMBER="$(get_version_as_number "$VERSION")"
  VERSION_0_9_5="$(get_version_as_number 0.9.5)"
  VERSION_1_0_0_ALPHA1="$(get_version_as_number 1.0.0-alpha1)"
  VERSION_1_0_0_ALPHA2="$(get_version_as_number 1.0.0-alpha2)"

  if [ "$VERSION" = "$STACKGRES_VERSION" ]
  then
    echo "$IMAGE_TAG"
  else
    if {
        [ "$VERSION_AS_NUMBER" -lt "$VERSION_1_0_0_ALPHA2" ] \
        && [ "$VERSION_AS_NUMBER" -ge "$VERSION_1_0_0_ALPHA1" ]
      } \
      || [ "$VERSION_AS_NUMBER" -lt "$VERSION_0_9_5" ]
    then
      echo "$VERSION-jvm"
    else
      echo "$VERSION"
    fi
  fi
}

get_operator_image() {
  local TAG
  local REPO
  TAG="$(get_operator_image_tag "$1")"
  REPO="$(get_operator_image_repository "$1")"
  echo "$REPO/operator:$TAG"
}

get_restapi_image() {
  local TAG
  local REPO
  TAG="$(get_operator_image_tag "$1")"
  REPO="$(get_operator_image_repository "$1")"
  echo "$REPO/restapi:$TAG"
}

get_adminui_image() {
  local TAG
  local REPO
  TAG="$(get_operator_image_tag "$1")"
  REPO="$(get_operator_image_repository "$1")"
  echo "$REPO/admin-ui:${TAG%-jvm}"
}

get_operator_helm_url() {
  local VERSION="$1"
  local VERSION_AS_NUMBER VERSION_0_9_2
  VERSION_AS_NUMBER="$(get_version_as_number "$VERSION")"
  VERSION_0_9_2="$(get_version_as_number 0.9.2)"
  if [ "$VERSION_AS_NUMBER" -le "$VERSION_0_9_2" ]
  then
    get_operator_helm_url_pre_0_9_3
    return
  fi
  echo "https://stackgres.io/downloads/stackgres-k8s/stackgres/$VERSION/helm/stackgres-operator.tgz"
}

get_operator_helm_url_pre_0_9_3() {
  echo "https://stackgres.io/downloads/stackgres-k8s/stackgres/$VERSION/helm-operator.tgz"
}

get_cluster_helm_url() {
  local VERSION="$1"
  local VERSION_AS_NUMBER VERSION_0_9_2
  VERSION_AS_NUMBER="$(get_version_as_number "$VERSION")"
  VERSION_0_9_2="$(get_version_as_number 0.9.2)"
  if [ "$VERSION_AS_NUMBER" -le "$VERSION_0_9_2" ]
  then
    echo "https://stackgres.io/downloads/stackgres-k8s/stackgres/$VERSION/demo-helm-cluster.tgz"
    return
  fi
  echo "https://stackgres.io/downloads/stackgres-k8s/stackgres/$VERSION/helm/stackgres-cluster-demo.tgz"
}

get_version_as_number() {
  notrace_function traceable_get_version_as_number "$1"
}

traceable_get_version_as_number() {
  local VERSION="$1"
  [ -n "$VERSION" ]

  local CARDINALITY
  CARDINALITY="$(printf '%s' "$VERSION" | tr '.' ' ' | wc -w)"
  local MAJOR=""
  local MINOR
  local PATCH="0"
  local SUFFIX=""
  local SUFFIX_VERSION="0"

  if [ "$CARDINALITY" -gt 3 ]
  then
    echo "Unexpeted version format for $VERSION"
    return 1
  fi

  MAJOR="$(echo "$VERSION" | cut -d . -f 1)"
  MINOR="$(echo "$VERSION" | cut -d . -f 2)"
  if [ "$CARDINALITY" -eq 3 ]
  then
    PATCH="$(echo "$VERSION" | cut -d . -f 3)"
    if [ "${PATCH#*-}" = "$PATCH" ]
    then
      SUFFIX=""
    else
      SUFFIX="${PATCH#*-}"
      PATCH="${PATCH%%-*}"
    fi
  else
    if [ "${MINOR#*-}" = "$MINOR" ]
    then
      SUFFIX=""
    else
      SUFFIX="${MINOR#*-}"
      MINOR="${MINOR%%-*}"
    fi
  fi

  if [ "${SUFFIX%-SNAPSHOT}" != "$SUFFIX" ]
  then
    SUFFIX="${SUFFIX%-SNAPSHOT}"
  fi

  if [ -z "$SUFFIX" ] || [ "$SUFFIX" = "SNAPSHOT" ]
  then
    SUFFIX=""
    SUFFIX_VERSION=0
  else
    if [ "${SUFFIX%%[0-9]*}" = "$SUFFIX" ]
    then
      echo "Unexpeted version format for $VERSION"
      return 1
    fi
    SUFFIX_VERSION="${SUFFIX#${SUFFIX%%[0-9]*}}"
    SUFFIX="${SUFFIX%$SUFFIX_VERSION}"
  fi

  local SUFFIX_MAJOR_VERSION
  if [ "$SUFFIX" = "alpha" ]
  then
    SUFFIX_MAJOR_VERSION=1
  elif [ "$SUFFIX" = "beta" ]
  then
    SUFFIX_MAJOR_VERSION=2
  elif [ "$SUFFIX" = "RC" ] || [ "$SUFFIX" = "rc" ]
  then
    SUFFIX_MAJOR_VERSION=3
  elif [ -z "$SUFFIX" ]
  then
    SUFFIX_MAJOR_VERSION=4
  else
    echo "Unexpeted version format for $VERSION"
    return 1
  fi

  printf "%03d%03d%03d%d%03d\n" "$MAJOR" "$MINOR" "$PATCH" "$SUFFIX_MAJOR_VERSION" "$SUFFIX_VERSION"
}

setup_versions() {
  LOCAL_STACKGRES_VERSION="$(sh stackgres-k8s/ci/build/version.sh)"
  STACKGRES_VERSION="${STACKGRES_VERSION:-$LOCAL_STACKGRES_VERSION}"
  if [ "$STACKGRES_VERSION" = "$LOCAL_STACKGRES_VERSION" ]
  then
    OPERATOR_VERSION_HELM_URL="${OPERATOR_VERSION_HELM_URL:-$OPERATOR_CHART_PATH}"
  else
    OPERATOR_VERSION_HELM_URL="${OPERATOR_VERSION_HELM_URL:-$(get_operator_helm_url "$STACKGRES_VERSION")}"
  fi
  STACKGRES_OPERATOR_IMAGE="$(get_operator_image "$STACKGRES_VERSION")"
  STACKGRES_RESTAPI_IMAGE="$(get_restapi_image "$STACKGRES_VERSION")"
  STACKGRES_ADMINUI_IMAGE="$(get_adminui_image "$STACKGRES_VERSION")"

  OPERATOR_PREVIOUS_VERSION_HELM_URL="${OPERATOR_PREVIOUS_VERSION_HELM_URL:-$(get_operator_helm_url "$STACKGRES_PREVIOUS_VERSION")}"
  CLUSTER_PREVIOUS_VERSION_HELM_URL="${CLUSTER_PREVIOUS_VERSION_HELM_URL:-$(get_cluster_helm_url "$STACKGRES_PREVIOUS_VERSION")}"
  STACKGRES_PREVIOUS_OPERATOR_IMAGE="$(get_operator_image "$STACKGRES_PREVIOUS_VERSION")"
  STACKGRES_PREVIOUS_RESTAPI_IMAGE="$(get_restapi_image "$STACKGRES_PREVIOUS_VERSION")"
  STACKGRES_PREVIOUS_ADMINUI_IMAGE="$(get_adminui_image "$STACKGRES_PREVIOUS_VERSION")"
  export LOCAL_STACKGRES_VERSION STACKGRES_VERSION STACKGRES_PREVIOUS_VERSION \
      OPERATOR_VERSION_HELM_URL OPERATOR_PREVIOUS_VERSION_HELM_URL \
      CLUSTER_PREVIOUS_VERSION_HELM_URL STACKGRES_PREVIOUS_OPERATOR_IMAGE \
      STACKGRES_PREVIOUS_RESTAPI_IMAGE STACKGRES_PREVIOUS_ADMINUI_IMAGE \
      STACKGRES_OPERATOR_IMAGE STACKGRES_RESTAPI_IMAGE STACKGRES_ADMINUI_IMAGE

  echo "StackGres local version is $LOCAL_STACKGRES_VERSION"
  echo "StackGres version used is $STACKGRES_VERSION"
  echo "* StackGres operator image used is $STACKGRES_OPERATOR_IMAGE"
  echo "* StackGres restapi image used is $STACKGRES_RESTAPI_IMAGE"
  echo "* StackGres admin-ui image used is $STACKGRES_ADMINUI_IMAGE"
  echo "Previous StackGres version used is $STACKGRES_PREVIOUS_VERSION"
  echo "* Previous StackGres operator image used is $STACKGRES_PREVIOUS_OPERATOR_IMAGE"
  echo "* Previous StackGres restapi image used is $STACKGRES_PREVIOUS_RESTAPI_IMAGE"
  echo "* Previous StackGres admin-ui image used is $STACKGRES_PREVIOUS_ADMINUI_IMAGE"
}

get_sgcluster_name() {
  get_cr_name "$1" sgclusters.stackgres.io
}

get_sgdistributedlogs_name() {
  get_cr_name "$1" sgdistributedlogs.stackgres.io
}

get_sgbackup_name() {
  get_cr_name "$1" sgbackups.stackgres.io
}

get_sgdbops_name() {
  get_cr_name "$1" sgdbops.stackgres.io
}

get_sgshardedcluster_name() {
  get_cr_name "$1" sgshardedclusters.stackgres.io
}

get_sgshardedbackup_name() {
  get_cr_name "$1" sgshardedbackups.stackgres.io
}

get_sgshardeddbops_name() {
  get_cr_name "$1" sgshardeddbops.stackgres.io
}

get_sgstreams_name() {
  get_cr_name "$1" sgstreams.stackgres.io
}

get_cr_name() {
  [ -n "$1" ] && [ -n "$2" ]
  if [ "$E2E_SET_MAX_LENGTH_NAMES" = true ]
  then
    MAX_LENGTH="$(kubectl get crd "$2" \
      -o jsonpath="{ .spec.versions[0].schema.openAPIV3Schema.properties.metadata.properties.name.maxLength }")"
    MAX_LENGTH="${MAX_LENGTH:-0}"
    CURRENT_LENGTH="$(printf '%s' "$1" | wc -c)"
    if [ "$MAX_LENGTH" -ge "$CURRENT_LENGTH" ]
    then
      printf '%s' "$1"
      local INDEX
      # shellcheck disable=SC2034
      for INDEX in $(seq 1 "$((MAX_LENGTH - CURRENT_LENGTH))")
      do
        printf 'x'
      done
    else
      printf '%s' "$1" | head -c "$MAX_LENGTH"
    fi
    return
  fi
  printf '%s' "$1"
}

set_max_length() {
  if [ "$E2E_SET_MAX_LENGTH_NAMES_PLUS_ONE" = true ]
  then
    local CRD_NAME
    for CRD_NAME in sgclusters.stackgres.io sgdistributedlogs.stackgres.io sgbackups.stackgres.io sgdbops.stackgres.io
    do
      kubectl get crd "$CRD_NAME" -o json | jq '
        if .spec.versions[0].schema.openAPIV3Schema.properties.metadata.properties.name.maxLength != null
        then (.spec.versions[0].schema.openAPIV3Schema.properties.metadata.properties.name.maxLength =
          (.spec.versions[0].schema.openAPIV3Schema.properties.metadata.properties.name.maxLength + 1))
	else . end' \
        | kubectl patch crd "$CRD_NAME" --type merge -p "$(cat)"
    done
  fi
}

set_helm_values_for_developer() {
  E2E_OPERATOR_EXTRA_OPTS_INDEX=13
  cat << EOF
    --set-string developer.extraEnv.SG_IMAGE_CLUSTER_CONTROLLER=${CLUSTER_CONTROLLER_IMAGE_NAME} 
    --set-string developer.extraOpts[0]=-Dquarkus.log.category."io.stackgres".level=DEBUG 
    --set-string developer.extraOpts[1]=-Dquarkus.log.category."io.quarkus".level=INFO 
    --set-string developer.extraOpts[2]=-Dquarkus.log.category."io.stackgres.collector".level=TRACE 
    --set-string developer.extraOpts[3]=-Dquarkus.log.category."io.stackgres.dbops".level=TRACE 
    --set-string developer.extraOpts[4]=-Dquarkus.log.category."io.stackgres.backup".level=TRACE 
    --set-string developer.extraOpts[5]=-Dquarkus.log.category."io.stackgres.wal-g".level=INFO 
    --set-string developer.extraOpts[6]=-Dquarkus.log.category."io.stackgres.patroni".level=TRACE 
    --set-string developer.extraOpts[7]=-Dquarkus.log.category."io.stackgres.fluent-bit".level=TRACE 
    --set-string developer.extraOpts[8]=-Dquarkus.log.category."io.stackgres.fluentd".level=TRACE 
    --set-string developer.extraOpts[9]=-Dquarkus.log.category."io.stackgres.prometheus-postgres-exporter".level=TRACE 
    --set-string developer.extraOpts[10]=-Dquarkus.log.category."okhttp3.logging.HttpLoggingInterceptor".level=$(
      # shellcheck disable=SC2015
      [ "$E2E_LOG_OPERATOR_HTTP" = true ] && echo TRACE || echo INFO) 
    --set-string developer.extraOpts[11]=-Dquarkus.log.category."stackgres-extensions-cache".level=DEBUG 
    --set-string developer.extraOpts[12]=-Dquarkus.log.category."io.stackgres.operator.conciliation".level=TRACE 
EOF
  if [ "$K8S_IP_FAMILY" = ipv6 ]
  then
  E2E_OPERATOR_EXTRA_OPTS_INDEX=15
  cat << EOF
    --set-string developer.extraOpts[13]=-Djava.net.preferIPv6Addresses=true 
    --set-string developer.extraOpts[14]=-Djava.net.preferIPv4Stack=false 
EOF
  fi
  if [ -n "$E2E_EXTRA_MOUNT_BUILD_PATH" ]
  then
    cat << EOF
    --set-string developer.patches.operator.volumes[0].name=app 
    --set-string developer.patches.operator.volumes[0].hostPath.path=$(realpath "$E2E_EXTRA_MOUNT_BUILD_PATH"/stackgres-k8s/src/operator/target/quarkus-app) 
    --set-string developer.patches.operator.volumeMounts[0].name=app 
    --set-string developer.patches.operator.volumeMounts[0].mountPath=/app/app 
    --set-string developer.patches.operator.volumeMounts[0].subPath=app 
    --set-string developer.patches.operator.volumeMounts[1].name=app 
    --set-string developer.patches.operator.volumeMounts[1].mountPath=/app/lib 
    --set-string developer.patches.operator.volumeMounts[1].subPath=lib 
    --set-string developer.patches.operator.volumeMounts[2].name=app 
    --set-string developer.patches.operator.volumeMounts[2].mountPath=/app/quarkus 
    --set-string developer.patches.operator.volumeMounts[2].subPath=quarkus 
    --set-string developer.patches.operator.volumeMounts[3].name=app 
    --set-string developer.patches.operator.volumeMounts[3].mountPath=/app/quarkus-run.jar 
    --set-string developer.patches.operator.volumeMounts[3].subPath=quarkus-run.jar 
    --set-string developer.patches.restapi.volumes[0].name=app 
    --set-string developer.patches.restapi.volumes[0].hostPath.path=$(realpath "$E2E_EXTRA_MOUNT_BUILD_PATH"/stackgres-k8s/src/restapi/target/quarkus-app) 
    --set-string developer.patches.restapi.volumeMounts[0].name=app 
    --set-string developer.patches.restapi.volumeMounts[0].mountPath=/app/app 
    --set-string developer.patches.restapi.volumeMounts[0].subPath=app 
    --set-string developer.patches.restapi.volumeMounts[1].name=app 
    --set-string developer.patches.restapi.volumeMounts[1].mountPath=/app/lib 
    --set-string developer.patches.restapi.volumeMounts[1].subPath=lib 
    --set-string developer.patches.restapi.volumeMounts[2].name=app 
    --set-string developer.patches.restapi.volumeMounts[2].mountPath=/app/quarkus 
    --set-string developer.patches.restapi.volumeMounts[2].subPath=quarkus 
    --set-string developer.patches.restapi.volumeMounts[3].name=app 
    --set-string developer.patches.restapi.volumeMounts[3].mountPath=/app/quarkus-run.jar 
    --set-string developer.patches.restapi.volumeMounts[3].subPath=quarkus-run.jar 
    --set-string developer.patches.adminui.volumes[0].name=admin 
    --set-string developer.patches.adminui.volumes[0].hostPath.path=$(realpath "$E2E_EXTRA_MOUNT_BUILD_PATH"/stackgres-k8s/src/admin-ui/target/public) 
    --set-string developer.patches.adminui.volumeMounts[0].name=admin 
    --set-string developer.patches.adminui.volumeMounts[0].mountPath=/opt/app-root/src/admin 
    --set-string developer.patches.clusterController.volumes[0].name=app 
    --set-string developer.patches.clusterController.volumes[0].hostPath.path=$(realpath "$E2E_EXTRA_MOUNT_BUILD_PATH"/stackgres-k8s/src/cluster-controller/target/quarkus-app) 
    --set-string developer.patches.clusterController.volumeMounts[0].name=app 
    --set-string developer.patches.clusterController.volumeMounts[0].mountPath=/app/app 
    --set-string developer.patches.clusterController.volumeMounts[0].subPath=app 
    --set-string developer.patches.clusterController.volumeMounts[1].name=app 
    --set-string developer.patches.clusterController.volumeMounts[1].mountPath=/app/lib 
    --set-string developer.patches.clusterController.volumeMounts[1].subPath=lib 
    --set-string developer.patches.clusterController.volumeMounts[2].name=app 
    --set-string developer.patches.clusterController.volumeMounts[2].mountPath=/app/quarkus 
    --set-string developer.patches.clusterController.volumeMounts[2].subPath=quarkus 
    --set-string developer.patches.clusterController.volumeMounts[3].name=app 
    --set-string developer.patches.clusterController.volumeMounts[3].mountPath=/app/quarkus-run.jar 
    --set-string developer.patches.clusterController.volumeMounts[3].subPath=quarkus-run.jar 
    --set-string developer.patches.stream.volumes[0].name=app 
    --set-string developer.patches.stream.volumes[0].hostPath.path=$(realpath "$E2E_EXTRA_MOUNT_BUILD_PATH"/stackgres-k8s/src/stream/target/quarkus-app) 
    --set-string developer.patches.stream.volumeMounts[0].name=app 
    --set-string developer.patches.stream.volumeMounts[0].mountPath=/app/app 
    --set-string developer.patches.stream.volumeMounts[0].subPath=app 
    --set-string developer.patches.stream.volumeMounts[1].name=app 
    --set-string developer.patches.stream.volumeMounts[1].mountPath=/app/lib 
    --set-string developer.patches.stream.volumeMounts[1].subPath=lib 
    --set-string developer.patches.stream.volumeMounts[2].name=app 
    --set-string developer.patches.stream.volumeMounts[2].mountPath=/app/quarkus 
    --set-string developer.patches.stream.volumeMounts[2].subPath=quarkus 
    --set-string developer.patches.stream.volumeMounts[3].name=app 
    --set-string developer.patches.stream.volumeMounts[3].mountPath=/app/quarkus-run.jar 
    --set-string developer.patches.stream.volumeMounts[3].subPath=quarkus-run.jar 
EOF
  fi
}
